<template>
  <div class="chat-container">
    <!-- 左侧对话列表 -->
    <div class="chat-sidebar">
      <AddChatButton @click="createNewChat"/>
      <el-menu
          :default-active="currentChatId"
          @select="handleSelectChat"
          class="chat-menu"
      >
        <el-menu-item
            v-for="chat in chatList"
            :key="chat.id"
            :index="chat.id"
        >
          <div class="chat-item">
            <span>{{ chat.title }}</span>
            <el-popover
                placement="right"
                width="150"
                trigger="hover"
            >
              <template #reference>
                <el-button type="text" class="operation-icon">
                  <i class="el-icon-more"></i>
                </el-button>
              </template>
              <div class="operation-menu">
                <el-button type="text" @click="pinChat(chat.id)">置顶</el-button>
                <el-button type="text" @click="deleteChat(chat.id)">删除</el-button>
                <el-button type="text" @click="saveChat(chat.id)">保存下载</el-button>
              </div>
            </el-popover>
          </div>
        </el-menu-item>
      </el-menu>
    </div>

    <!-- 右侧主聊天区域 -->
    <div class="chat-main">
      <div class="message-container">
        <BubbleList
            :list="messages"
            max-height="calc(100vh - 180px)"
            class="bubble-list"
        >
          <template #avatar="{ item }">
            <div class="avatar-wrapper">
              <el-avatar
                  v-if="item.role === 'assistant'"
                  shape="square"
                  :size="32"
                  :src="avatarAI"
              />
              <el-avatar
                  v-else
                  shape="circle"
                  :size="32"
                  :src="userAvatar"
              />
            </div>
          </template>
          <!-- 自定义气泡头像 -->
          <template #header="{ item }">
            <div class="header-wrapper">
              <span class="header-name">
                {{ item.role === 'assistant' ? '颐养智策' : '用户' }}
              </span>
            </div>
          </template>
          <!-- 自定义气泡内容 -->
          <template #content="{ item }">
            <div
                class="content-wrapper"
                :class="{ 'user-content': item.role === 'user', 'assistant-content': item.role === 'assistant' }"
            >
              <div v-if="item.isMarkdown" v-html="renderMarkdown(item.content)"></div>
              <div v-else>{{ item.content }}</div>
            </div>
          </template>
          <!-- 自定义底部 -->
          <template #footer="{ item }">
            <div class="footer-wrapper">
              <div v-if="item.role === 'user'" class="footer-time">
                {{ item.timestamp || '' }}
              </div>
              <div v-if="item.role === 'assistant' && !item.typing && item.content !== chatStore.indexContent"
                   class="footer-actions">
                <div class="footer-container">
                  <el-button type="info" icon="Refresh" size="small" @click="refreshChat()" circle/>
                  <el-button type="success" icon="Search" size="small" @click="searchChat()" circle/>
                  <el-button type="warning" icon="Star" size="small" @click="collection()" circle/>
                  <el-tooltip
                      :content="copyStatus === 'success' ? '✨已复制' : '复制'"
                      placement="top"
                      :effect="copyStatus === 'success' ? 'dark' : 'light'"
                  >
                    <el-button color="#626aef" icon="DocumentCopy" size="small" @click="copyReply(item.content)"
                               circle/>
                  </el-tooltip>
                </div>
              </div>
            </div>
          </template>
        </BubbleList>
      </div>
      <Sender
          class="sender"
          v-model="inputMessage"
          placeholder="输入您的问题..."
          :auto-size="{ minRows: 2, maxRows: 5 }"
          allow-speech
          @cancel=""
          :disabled="!isConnected || isBotTyping"
          :loading="!isConnected || isBotTyping"
          headerAnimationTimer="300"
          @submit="sendMessage"
          clearable
      />
    </div>
  </div>

</template>

<script>
import SockJS from 'sockjs-client/dist/sockjs.min';
import {marked} from 'marked';
import {Marked} from 'marked';
import {markedHighlight} from 'marked-highlight';
import DOMPurify from 'dompurify';
import {BubbleList, Sender, Welcome} from 'vue-element-plus-x';
import {DocumentCopy, Refresh, Search, Star} from '@element-plus/icons-vue';
import Prism from 'prismjs';
import 'prismjs/components';
import 'prismjs/components/prism-java';
import 'prismjs/components/prism-javascript';
import 'prismjs/plugins/line-numbers/prism-line-numbers';
import 'prismjs/plugins/toolbar/prism-toolbar';
import 'prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard';
import 'prismjs/themes/prism-okaidia.css';
import 'prismjs/plugins/line-numbers/prism-line-numbers.css';
import {useChatStore} from '../../store/chat';
import {ElMessage, ElMessageBox} from "element-plus";
import AddChatButton from "../../components/AddChatButton.vue";
import logoImage from '../../assets/images/logo2.png'
import {getLoginerInfo} from "../../service/userApi.js";

marked.setOptions({
  gfm: true,
  breaks: true,
});

const markedInstance = new Marked(
    markedHighlight({
      langPrefix: 'language-',
      highlight(code, lang) {
        return Prism.highlight(code, Prism.languages[lang] || Prism.languages.plaintext, lang);
      },
    }),
    {
      gfm: true,
      breaks: true,
    }
);


export default {
  name: 'ChatWindow',
  components: {
    AddChatButton,
    Sender,
    BubbleList,
    Welcome,
    Refresh,
    Search,
    Star,
    DocumentCopy,
  },
  data() {
    return {
      ws: null,
      inputMessage: '',
      isConnected: false,
      reconnectAttempts: 0,
      maxReconnectAttempts: 5,
      currentResponse: '',
      copyStatus: '', // 用于记录复制状态
      isBotTyping: false,
      botTypingTimeout: null,
      selectedModel: 'deepseek-ai/DeepSeek-R1-Distill-Qwen-7B',
      maxTokens: '10240',
      avatarAI: logoImage,
      userAvatar: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
    };
  },
  computed: {
    chatStore() {
      return useChatStore();
    },
    chatList() {
      return this.chatStore.chatList;
    },
    currentChatId() {
      return this.chatStore.currentChatId;
    },
    messages() {
      return this.chatStore.getCurrentMessages();
    },
  },
  mounted() {
    document.body.setAttribute('data-prismjs-copy', '复制');
    document.body.setAttribute('data-prismjs-copy-success', '已复制！');
    document.body.setAttribute('data-prismjs-copy-error', '复制失败，请手动复制');
    document.body.setAttribute('data-prismjs-copy-timeout', '2000');

    this.chatStore.loadChatList();
    if (this.chatList.length === 0) {
      const initialChatId = this.chatStore.addChat();
      this.chatStore.selectChat(initialChatId);
    } else {
      this.chatStore.selectChat(this.chatList[0].id);
    }
    // 初始化websocket连接
    this.initWebSocket();
    // 刷新用户头像
    this.refreshUserImage();
  },
  beforeDestroy() {
    this.closeWebSocket();
    if (this.ws) this.ws.onmessage = null;
    if (this.botTypingTimeout) clearTimeout(this.botTypingTimeout);
  },
  methods: {
    initWebSocket() {
      try {
        this.ws = new SockJS('/ws/chat?userId=' + this.chatStore.userId);
        this.ws.onopen = () => {
          console.log('WebSocket连接已建立');
          this.isConnected = true;
          this.reconnectAttempts = 0;
        };
        this.ws.onmessage = (event) => {
          if (event.data) {
            let content = event.data;
            try {
              const response = JSON.parse(event.data);
              content = typeof response.content === 'string' ? response.content : String(response.content || event.data);
            } catch (e) {
              content = String(event.data);
            }
            this.appendMessage('assistant', content, false);
          }
        };
        this.ws.onerror = (error) => {
          console.error('WebSocket错误:', error);
          this.isConnected = false;
          this.appendMessage('error', '连接发生错误，正在尝试重新连接...', true);
        };
        this.ws.onclose = () => {
          this.isConnected = false;
          this.isBotTyping = false;
          this.reconnect();
        };
      } catch (error) {
        console.error('WebSocket初始化失败:', error);
        this.isConnected = false;
        this.appendMessage('error', '连接初始化失败，请刷新页面重试', true);
      }
    },
    closeWebSocket() {
      console.log('关闭WebSocket连接...')
      if (this.ws) {
        this.ws.close();
        this.ws = null;
      }
    },
    reconnect() {
      console.log('尝试重新连接...')
      if (this.reconnectAttempts < this.maxReconnectAttempts) {
        this.reconnectAttempts++;
        setTimeout(() => this.initWebSocket(), 3000);
      }
    },
    // 发送消息
    sendMessage() {
      // 如果输入内容为空那么弹出提示
      if (!this.inputMessage.trim()) {
        ElMessage.warning('输入内容不能为空');
        return;
      }
      if (this.inputMessage.trim() && this.isConnected && !this.isBotTyping) {
        // 先将用户的输入添加到消息列表中，并设置isComplete为true
        this.appendMessage('user', this.inputMessage, true);
        this.isBotTyping = true;
        this.inputMessage = '';
        const currentMessages = this.chatStore.getCurrentMessages();
        const messageList = currentMessages.map(message => ({
          role: message.role,
          content: message.content
        }));
        const wsMessage = {
          model: this.selectedModel,
          maxTokens: this.maxTokens,
          messageList: messageList
        };
        this.ws.send(JSON.stringify(wsMessage));
        this.chatStore.appendMessageToCurrent('assistant', '', false);
        this.scrollToBottom();
      }
    },
    // 添加消息到消息列表
    appendMessage(type, content, isComplete = false) {
      if (type === 'user' || isComplete) {
        this.chatStore.appendMessageToCurrent(type, content, isComplete);
      } else if (type === 'assistant' && !isComplete) {
        this.chatStore.updateCurrentBotMessage(content);
        this.scrollToBottom();
        if (this.botTypingTimeout) clearTimeout(this.botTypingTimeout);
        this.botTypingTimeout = setTimeout(() => {
          this.chatStore.completeCurrentBotMessage();
          this.isBotTyping = false;
          this.$nextTick(() => Prism.highlightAll());
        }, 1800);
      }
      this.$nextTick(() => {
        Prism.highlightAll();
        this.scrollToBottom();
      });
    },
    // 切换对话
    handleSelectChat(chatId) {
      this.chatStore.selectChat(chatId);
      this.$nextTick(() => {
        Prism.highlightAll();
        this.scrollToBottom();
      });
    },
    // 新建对话
    createNewChat() {
      const newChatId = this.chatStore.addChat();
      this.chatStore.selectChat(newChatId);
      this.$nextTick(() => {
        Prism.highlightAll();
        this.scrollToBottom();
      });
    },
    pinChat(chatId) {
      this.chatStore.pinChat(chatId);
    },
    // 删除对话
    async deleteChat(chatId) {
      try {
        await ElMessageBox.confirm('确定删除此对话？', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning',
        });

        this.chatStore.deleteChat(chatId);
        if (this.currentChatId === chatId) {
          const remainingChats = this.chatList;
          if (remainingChats.length > 0) {
            this.chatStore.selectChat(remainingChats[0].id);
          } else {
            const newChatId = this.chatStore.addChat();
            this.chatStore.selectChat(newChatId);
          }
        }
        await this.$nextTick(() => {
          Prism.highlightAll();
          this.scrollToBottom();
        });
      } catch (error) {
        // 用户点击了取消，不做任何操作
        console.log('取消删除');
      }
    },
    // 保存对话
    saveChat(chatId) {
      const chat = this.chatList.find(c => c.id === chatId);
      if (chat) {
        const blob = new Blob([JSON.stringify(chat.messages, null, 2)], {type: 'application/json'});
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `${chat.title}.json`;
        a.click();
        URL.revokeObjectURL(url);
      }
    },
    // 将Markdown转换为HTML
    renderMarkdown(content) {
      if (typeof content !== 'string') {
        content = String(content);
      }
      try {
        const html = markedInstance.parse(content);
        // !!!因为难以操作模型输出符合图片加载的内容，因此在这需要对<a>内容进行字符串替换为<img>从而实现加载图片链接
        const modifiedHtml = html.replace(/<a href/g, '<img style="border-radius: 8px; max-width: 140px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);" src')
            .replace( /<\/a>/g, '</img>');
        return DOMPurify.sanitize(modifiedHtml);
      } catch (e) {
        console.error('Markdown 解析失败:', e);
        return DOMPurify.sanitize(content);
      }
    },
    // 滚动到底部
    scrollToBottom() {
      this.$nextTick(() => {
        const container = document.querySelector('.bubble-list');
        if (container) container.scrollTo({top: container.scrollHeight, behavior: 'smooth'});
      });
    },
    // 复制回复
    async copyReply(content) {
      try {
        if (navigator.clipboard) {
          await navigator.clipboard.writeText(content);
        } else {
          // 回退到 document.execCommand('copy')
          const textarea = document.createElement('textarea');
          textarea.value = content;
          document.body.appendChild(textarea);
          textarea.select();
          document.execCommand('copy');
          document.body.removeChild(textarea);
        }
        this.copyStatus = 'success';
      } catch (error) {
        console.error('复制失败:', error);
        this.copyStatus = 'error';
      } finally {
        setTimeout(() => {
          this.copyStatus = '';
        }, 2000);
      }
    },
    async refreshChat() {
      this.isBotTyping = true;
      const currentMessages = this.chatStore.getCurrentMessages();
      //移除最后一条助手消息
      const lastMessage = currentMessages[currentMessages.length - 1];
      if (lastMessage.role === 'assistant') {
        this.chatStore.removeLastMessage(); // 假设chatStore有此方法，用于移除最后一条消息
      }
      const messageList = currentMessages.map(message => ({
        role: message.role,
        content: message.content
      }));
      const wsMessage = {
        model: this.selectedModel,
        maxTokens: this.maxTokens,
        messageList: messageList
      };
      this.ws.send(JSON.stringify(wsMessage));
      this.chatStore.appendMessageToCurrent('assistant', '', false);
      this.scrollToBottom();
    },
    async searchChat() {
    },
    async collection() {
    },
    async refreshUserImage() {
      const res = await getLoginerInfo();
      this.userAvatar = res.data.avatar; // 更新用户头像
    }
  },
};
</script>

<style scoped>
.chat-container {
  max-width: 100%;
  width: 1000px;
  margin: 0 auto;
  height: calc(100vh - 84px);
  background: #292932;
  color: #ffffff;
  display: flex;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}

.chat-sidebar {
  width: 190px;
  background: #4d648d;
  border-right: 1px solid #3b82f6;
  overflow-y: auto;
  padding: 10px;
}

.chat-menu {
  margin-top: 14px;
  background: transparent;
  border: none;
}

.chat-menu .el-menu-item {
  color: #ffffff;
  font-size: 14px;
  padding: 10px 6px;
  border-radius: 5px;
  margin-bottom: 5px;
}

.chat-menu .el-menu-item:hover {
  background-color: #acc2ef;
}

.chat-menu .el-menu-item.is-active {
  background-color: #1F3A5F;
  color: #ffffff;
}

.chat-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
}

.operation-icon {
  color: #ffffff;
  font-size: 16px;
  padding: 0;
  margin-left: 10px;
  visibility: hidden;
}

.el-menu-item:hover .operation-icon {
  visibility: visible;
}

.operation-menu {
  background: #4d648d;
  display: flex;
  flex-direction: column;
}

.operation-menu .el-button {
  text-align: left;
  padding: 5px 10px;
  color: #ffffff;
}

.operation-menu .el-button:hover {
  background-color: #2d3748;
}

.chat-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: linear-gradient(135deg, #a1c4fd, #ffb787);

}

.message-container {
  flex: 1;
  margin: 10px;
  overflow-y: auto;
}

.header-name {
  color: #4b4848;
}

.bubble-list {
  max-width: 100%;
  overflow-y: auto;
}

.content-wrapper {
  margin-top: -10px;
  margin-bottom: -10px;
  max-width: 100%;
  width: 100%;
  text-align: left !important;
}

.content-wrapper pre {
  background: #1e1e1e;
  border-radius: 5px;
  overflow-x: auto;
}


.footer-wrapper {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 2px;
}

.footer-time {
  margin-top: -8px;
  font-size: 12px;
  color: #9ca3af;
}

.footer-actions {
  margin-top: -8px;
  display: flex;
}

.sender {
  width: 100%;
  border-radius: 10px;
  background: #56647b;
}

.avatar-wrapper {
  width: 48px;
  height: 48px;
}

@keyframes typing {
  0% {
    width: 0;
  }
  100% {
    width: 100%;
  }
}

</style>